home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_weapons.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  47.3 KB  |  1,802 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_weapons.c -- events and effects dealing with weapons
  4. #include "cg_local.h"
  5.  
  6. /*
  7. ==========================
  8. CG_MachineGunEjectBrass
  9. ==========================
  10. */
  11. static void CG_MachineGunEjectBrass( centity_t *cent ) {
  12.     localEntity_t    *le;
  13.     refEntity_t        *re;
  14.     vec3_t            velocity, xvelocity;
  15.     vec3_t            offset, xoffset;
  16.     float            waterScale = 1.0f;
  17.     vec3_t            v[3];
  18.  
  19.     if ( cg_brassTime.integer <= 0 ) {
  20.         return;
  21.     }
  22.  
  23.     le = CG_AllocLocalEntity();
  24.     re = &le->refEntity;
  25.  
  26.     velocity[0] = 0;
  27.     velocity[1] = -50 + 40 * crandom();
  28.     velocity[2] = 100 + 50 * crandom();
  29.  
  30.     le->leType = LE_FRAGMENT;
  31.     le->startTime = cg.time;
  32.     le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random();
  33.  
  34.     le->pos.trType = TR_GRAVITY;
  35.     le->pos.trTime = cg.time - (rand()&15);
  36.  
  37.     AnglesToAxis( cent->lerpAngles, v );
  38.  
  39.     offset[0] = 8;
  40.     offset[1] = -4;
  41.     offset[2] = 24;
  42.  
  43.     xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  44.     xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  45.     xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  46.     VectorAdd( cent->lerpOrigin, xoffset, re->origin );
  47.  
  48.     VectorCopy( re->origin, le->pos.trBase );
  49.  
  50.     if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
  51.         waterScale = 0.10;
  52.     }
  53.  
  54.     xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
  55.     xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
  56.     xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
  57.     VectorScale( xvelocity, waterScale, le->pos.trDelta );
  58.  
  59.     AxisCopy( axisDefault, re->axis );
  60.     re->hModel = cgs.media.machinegunBrassModel;
  61.  
  62.     le->bounceFactor = 0.4 * waterScale;
  63.  
  64.     le->angles.trType = TR_LINEAR;
  65.     le->angles.trTime = cg.time;
  66.     le->angles.trBase[0] = rand()&31;
  67.     le->angles.trBase[1] = rand()&31;
  68.     le->angles.trBase[2] = rand()&31;
  69.     le->angles.trDelta[0] = 2;
  70.     le->angles.trDelta[1] = 1;
  71.     le->angles.trDelta[2] = 0;
  72.  
  73.     le->leFlags = LEF_TUMBLE;
  74.     le->leBounceSoundType = LEBS_BRASS;
  75.     le->leMarkType = LEMT_NONE;
  76. }
  77.  
  78. /*
  79. ==========================
  80. CG_ShotgunEjectBrass
  81. ==========================
  82. */
  83. static void CG_ShotgunEjectBrass( centity_t *cent ) {
  84.     localEntity_t    *le;
  85.     refEntity_t        *re;
  86.     vec3_t            velocity, xvelocity;
  87.     vec3_t            offset, xoffset;
  88.     vec3_t            v[3];
  89.     int                i;
  90.  
  91.     if ( cg_brassTime.integer <= 0 ) {
  92.         return;
  93.     }
  94.  
  95.     for ( i = 0; i < 2; i++ ) {
  96.         float    waterScale = 1.0f;
  97.  
  98.         le = CG_AllocLocalEntity();
  99.         re = &le->refEntity;
  100.  
  101.         velocity[0] = 60 + 60 * crandom();
  102.         if ( i == 0 ) {
  103.             velocity[1] = 40 + 10 * crandom();
  104.         } else {
  105.             velocity[1] = -40 + 10 * crandom();
  106.         }
  107.         velocity[2] = 100 + 50 * crandom();
  108.  
  109.         le->leType = LE_FRAGMENT;
  110.         le->startTime = cg.time;
  111.         le->endTime = le->startTime + cg_brassTime.integer*3 + cg_brassTime.integer * random();
  112.  
  113.         le->pos.trType = TR_GRAVITY;
  114.         le->pos.trTime = cg.time;
  115.  
  116.         AnglesToAxis( cent->lerpAngles, v );
  117.  
  118.         offset[0] = 8;
  119.         offset[1] = 0;
  120.         offset[2] = 24;
  121.  
  122.         xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0];
  123.         xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1];
  124.         xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2];
  125.         VectorAdd( cent->lerpOrigin, xoffset, re->origin );
  126.         VectorCopy( re->origin, le->pos.trBase );
  127.         if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) {
  128.             waterScale = 0.10;
  129.         }
  130.  
  131.         xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0];
  132.         xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1];
  133.         xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2];
  134.         VectorScale( xvelocity, waterScale, le->pos.trDelta );
  135.  
  136.         AxisCopy( axisDefault, re->axis );
  137.         re->hModel = cgs.media.shotgunBrassModel;
  138.         le->bounceFactor = 0.3;
  139.  
  140.         le->angles.trType = TR_LINEAR;
  141.         le->angles.trTime = cg.time;
  142.         le->angles.trBase[0] = rand()&31;
  143.         le->angles.trBase[1] = rand()&31;
  144.         le->angles.trBase[2] = rand()&31;
  145.         le->angles.trDelta[0] = 1;
  146.         le->angles.trDelta[1] = 0.5;
  147.         le->angles.trDelta[2] = 0;
  148.  
  149.         le->leFlags = LEF_TUMBLE;
  150.         le->leBounceSoundType = LEBS_BRASS;
  151.         le->leMarkType = LEMT_NONE;
  152.     }
  153. }
  154.  
  155.  
  156. /*
  157. ==========================
  158. CG_RailTrail
  159. ==========================
  160. */
  161. void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end ) {
  162.     localEntity_t    *le;
  163.     refEntity_t        *re;
  164.  
  165.     //
  166.     // rings
  167.     //
  168.     le = CG_AllocLocalEntity();
  169.     re = &le->refEntity;
  170.  
  171.     le->leType = LE_FADE_RGB;
  172.     le->startTime = cg.time;
  173.     le->endTime = cg.time + cg_railTrailTime.value;
  174.     le->lifeRate = 1.0 / ( le->endTime - le->startTime );
  175.  
  176.     re->shaderTime = cg.time / 1000.0f;
  177.     re->reType = RT_RAIL_RINGS;
  178.     re->customShader = cgs.media.railRingsShader;
  179.     
  180.     VectorCopy( start, re->origin );
  181.     VectorCopy( end, re->oldorigin );
  182.  
  183.     // nudge down a bit so it isn't exactly in center
  184.     re->origin[2] -= 8;
  185.     re->oldorigin[2] -= 8;
  186.  
  187.     le->color[0] = ci->color[0] * 0.75;
  188.     le->color[1] = ci->color[1] * 0.75;
  189.     le->color[2] = ci->color[2] * 0.75;
  190.     le->color[3] = 1.0f;
  191.  
  192.     AxisClear( re->axis );
  193.  
  194.     //
  195.     // core
  196.     //
  197.     le = CG_AllocLocalEntity();
  198.     re = &le->refEntity;
  199.  
  200.     le->leType = LE_FADE_RGB;
  201.     le->startTime = cg.time;
  202.     le->endTime = cg.time + cg_railTrailTime.value;
  203.     le->lifeRate = 1.0 / ( le->endTime - le->startTime );
  204.  
  205.     re->shaderTime = cg.time / 1000.0f;
  206.     re->reType = RT_RAIL_CORE;
  207.     re->customShader = cgs.media.railCoreShader;
  208.  
  209.     VectorCopy( start, re->origin );
  210.     VectorCopy( end, re->oldorigin );
  211.  
  212.     // nudge down a bit so it isn't exactly in center
  213.     re->origin[2] -= 8;
  214.     re->oldorigin[2] -= 8;
  215.  
  216.     le->color[0] = ci->color[0] * 0.75;
  217.     le->color[1] = ci->color[1] * 0.75;
  218.     le->color[2] = ci->color[2] * 0.75;
  219.     le->color[3] = 1.0f;
  220.  
  221.     AxisClear( re->axis );
  222. }
  223.  
  224. /*
  225. ==========================
  226. CG_RocketTrail
  227. ==========================
  228. */
  229. static void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) {
  230.     int        step;
  231.     vec3_t    origin, lastPos;
  232.     int        t;
  233.     int        startTime, contents;
  234.     int        lastContents;
  235.     entityState_t    *es;
  236.     vec3_t    up;
  237.     localEntity_t    *smoke;
  238.  
  239.     up[0] = 0;
  240.     up[1] = 0;
  241.     up[2] = 0;
  242.  
  243.     step = 50;
  244.  
  245.     es = &ent->currentState;
  246.     startTime = ent->trailTime;
  247.     t = step * ( (startTime + step) / step );
  248.  
  249.     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  250.     contents = CG_PointContents( origin, -1 );
  251.  
  252.     // if object (e.g. grenade) is stationary, don't toss up smoke
  253.     if ( es->pos.trType == TR_STATIONARY ) {
  254.         ent->trailTime = cg.time;
  255.         return;
  256.     }
  257.  
  258.     BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos );
  259.     lastContents = CG_PointContents( lastPos, -1 );
  260.  
  261.     ent->trailTime = cg.time;
  262.  
  263.     if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
  264.         if ( contents & lastContents & CONTENTS_WATER ) {
  265.             CG_BubbleTrail( lastPos, origin, 8 );
  266.         }
  267.         return;
  268.     }
  269.  
  270.     for ( ; t <= ent->trailTime ; t += step ) {
  271.         BG_EvaluateTrajectory( &es->pos, t, lastPos );
  272.  
  273.         smoke = CG_SmokePuff( lastPos, up, 
  274.                       wi->trailRadius, 
  275.                       1, 1, 1, 0.33,
  276.                       wi->wiTrailTime, 
  277.                       t,
  278.                       0, 
  279.                       cgs.media.smokePuffShader );
  280.         // use the optimized local entity add
  281.         smoke->leType = LE_SCALE_FADE;
  282.     }
  283.  
  284. }
  285.  
  286. /*
  287. ==========================
  288. CG_GrappleTrail
  289. ==========================
  290. */
  291. void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ) {
  292.     vec3_t    origin;
  293.     entityState_t    *es;
  294.     vec3_t            forward, up;
  295.     refEntity_t        beam;
  296.  
  297.     es = &ent->currentState;
  298.  
  299.     BG_EvaluateTrajectory( &es->pos, cg.time, origin );
  300.     ent->trailTime = cg.time;
  301.  
  302.     memset( &beam, 0, sizeof( beam ) );
  303.     //FIXME adjust for muzzle position
  304.     VectorCopy ( cg_entities[ ent->currentState.otherEntityNum ].lerpOrigin, beam.origin );
  305.     beam.origin[2] += 26;
  306.     AngleVectors( cg_entities[ ent->currentState.otherEntityNum ].lerpAngles, forward, NULL, up );
  307.     VectorMA( beam.origin, -6, up, beam.origin );
  308.     VectorCopy( origin, beam.oldorigin );
  309.  
  310.     if (Distance( beam.origin, beam.oldorigin ) < 64 )
  311.         return; // Don't draw if close
  312.  
  313.     beam.reType = RT_LIGHTNING;
  314.     beam.customShader = cgs.media.lightningShader;
  315.  
  316.     AxisClear( beam.axis );
  317.     beam.shaderRGBA[0] = 0xff;
  318.     beam.shaderRGBA[1] = 0xff;
  319.     beam.shaderRGBA[2] = 0xff;
  320.     beam.shaderRGBA[3] = 0xff;
  321.     trap_R_AddRefEntityToScene( &beam );
  322. }
  323.  
  324. /*
  325. ==========================
  326. CG_GrenadeTrail
  327. ==========================
  328. */
  329. static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) {
  330.     CG_RocketTrail( ent, wi );
  331. }
  332.  
  333.  
  334. /*
  335. =================
  336. CG_RegisterWeapon
  337.  
  338. The server says this item is used on this level
  339. =================
  340. */
  341. void CG_RegisterWeapon( int weaponNum ) {
  342.     weaponInfo_t    *weaponInfo;
  343.     gitem_t            *item, *ammo;
  344.     char            path[MAX_QPATH];
  345.     vec3_t            mins, maxs;
  346.     int                i;
  347.  
  348.     weaponInfo = &cg_weapons[weaponNum];
  349.  
  350.     if ( weaponNum == 0 ) {
  351.         return;
  352.     }
  353.  
  354.     if ( weaponInfo->registered ) {
  355.         return;
  356.     }
  357.  
  358.     memset( weaponInfo, 0, sizeof( *weaponInfo ) );
  359.     weaponInfo->registered = qtrue;
  360.  
  361.     for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
  362.         if ( item->giType == IT_WEAPON && item->giTag == weaponNum ) {
  363.             weaponInfo->item = item;
  364.             break;
  365.         }
  366.     }
  367.     if ( !item->classname ) {
  368.         CG_Error( "Couldn't find weapon %i", weaponNum );
  369.     }
  370.     CG_RegisterItemVisuals( item - bg_itemlist );
  371.  
  372.     // load cmodel before model so filecache works
  373.     weaponInfo->weaponModel = trap_R_RegisterModel( item->world_model[0] );
  374.  
  375.     // calc midpoint for rotation
  376.     trap_R_ModelBounds( weaponInfo->weaponModel, mins, maxs );
  377.     for ( i = 0 ; i < 3 ; i++ ) {
  378.         weaponInfo->weaponMidpoint[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] );
  379.     }
  380.  
  381.     weaponInfo->weaponIcon = trap_R_RegisterShader( item->icon );
  382.     weaponInfo->ammoIcon = trap_R_RegisterShader( item->icon );
  383.  
  384.     for ( ammo = bg_itemlist + 1 ; ammo->classname ; ammo++ ) {
  385.         if ( ammo->giType == IT_AMMO && ammo->giTag == weaponNum ) {
  386.             break;
  387.         }
  388.     }
  389.     if ( ammo->classname && ammo->world_model[0] ) {
  390.         weaponInfo->ammoModel = trap_R_RegisterModel( ammo->world_model[0] );
  391.     }
  392.  
  393.     strcpy( path, item->world_model[0] );
  394.     COM_StripExtension( path, path );
  395.     strcat( path, "_flash.md3" );
  396.     weaponInfo->flashModel = trap_R_RegisterModel( path );
  397.  
  398.     if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
  399.         strcpy( path, item->world_model[0] );
  400.         COM_StripExtension( path, path );
  401.         strcat( path, "_barrel.md3" );
  402.         weaponInfo->barrelModel = trap_R_RegisterModel( path );
  403.     }
  404.  
  405.     strcpy( path, item->world_model[0] );
  406.     COM_StripExtension( path, path );
  407.     strcat( path, "_hand.md3" );
  408.     weaponInfo->handsModel = trap_R_RegisterModel( path );
  409.  
  410.     if ( !weaponInfo->handsModel ) {
  411.         weaponInfo->handsModel = trap_R_RegisterModel( "models/weapons2/shotgun/shotgun_hand.md3" );
  412.     }
  413.  
  414.     switch ( weaponNum ) {
  415.     case WP_GAUNTLET:
  416.         MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 1 );
  417.         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav" );
  418.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/melee/fstatck.wav" );
  419.         break;
  420.  
  421.     case WP_LIGHTNING:
  422.         MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 1 );
  423.         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav" );
  424.         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/lightning/lg_hum.wav" );
  425.  
  426.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/lightning/lg_fire.wav" );
  427.         cgs.media.lightningShader = trap_R_RegisterShader( "lightningBolt" );
  428.         cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" );
  429.         cgs.media.sfx_lghit1 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit.wav" );
  430.         cgs.media.sfx_lghit2 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit2.wav" );
  431.         cgs.media.sfx_lghit3 = trap_S_RegisterSound( "sound/weapons/lightning/lg_hit3.wav" );
  432.  
  433.         break;
  434.  
  435.     case WP_GRAPPLING_HOOK:
  436.         MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 1 );
  437.         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
  438.         weaponInfo->missileTrailFunc = CG_GrappleTrail;
  439.         weaponInfo->missileDlight = 200;
  440.         weaponInfo->wiTrailTime = 2000;
  441.         weaponInfo->trailRadius = 64;
  442.         MAKERGB( weaponInfo->missileDlightColor, 1, 1, 0.5 );
  443.         MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 0.5 );
  444.         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/melee/fsthum.wav" );
  445.         weaponInfo->firingSound = trap_S_RegisterSound( "sound/weapons/melee/fstrun.wav" );
  446.         break;
  447.  
  448.     case WP_MACHINEGUN:
  449.         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
  450.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf1b.wav" );
  451.         weaponInfo->flashSound[1] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf2b.wav" );
  452.         weaponInfo->flashSound[2] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf3b.wav" );
  453.         weaponInfo->flashSound[3] = trap_S_RegisterSound( "sound/weapons/machinegun/machgf4b.wav" );
  454.         weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
  455.         cgs.media.bulletExplosionShader = trap_R_RegisterShader( "bulletExplosion" );
  456.         break;
  457.  
  458.     case WP_SHOTGUN:
  459.         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 0 );
  460.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/shotgun/sshotf1b.wav" );
  461.         weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass;
  462.         break;
  463.  
  464.     case WP_ROCKET_LAUNCHER:
  465.         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/rocket/rocket.md3" );
  466.         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav" );
  467.         weaponInfo->missileTrailFunc = CG_RocketTrail;
  468.         weaponInfo->missileDlight = 200;
  469.         weaponInfo->wiTrailTime = 2000;
  470.         weaponInfo->trailRadius = 64;
  471.         MAKERGB( weaponInfo->missileDlightColor, 1, 0.75, 0 );
  472.         MAKERGB( weaponInfo->flashDlightColor, 1, 0.75, 0 );
  473.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav" );
  474.         cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" );
  475.         break;
  476.  
  477.     case WP_GRENADE_LAUNCHER:
  478.         weaponInfo->missileModel = trap_R_RegisterModel( "models/ammo/grenade1.md3" );
  479.         weaponInfo->missileTrailFunc = CG_GrenadeTrail;
  480.         weaponInfo->wiTrailTime = 700;
  481.         weaponInfo->trailRadius = 32;
  482.         MAKERGB( weaponInfo->flashDlightColor, 1, 0.7, 0.5 );
  483.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/grenade/grenlf1a.wav" );
  484.         cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" );
  485.         break;
  486.  
  487.     case WP_PLASMAGUN:
  488.         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/plasma/lasfly.wav" );
  489.         MAKERGB( weaponInfo->flashDlightColor, 0.6, 0.6, 1 );
  490.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/plasma/hyprbf1a.wav" );
  491.         cgs.media.plasmaExplosionShader = trap_R_RegisterShader( "plasmaExplosion" );
  492.         break;
  493.  
  494.     case WP_RAILGUN:
  495.         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/railgun/rg_hum.wav" );
  496.         MAKERGB( weaponInfo->flashDlightColor, 1, 0.5, 0 );
  497.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/railgun/railgf1a.wav" );
  498.         cgs.media.railExplosionShader = trap_R_RegisterShader( "railExplosion" );
  499.         cgs.media.railRingsShader = trap_R_RegisterShader( "railDisc" );
  500.         cgs.media.railCoreShader = trap_R_RegisterShader( "railCore" );
  501.         break;
  502.  
  503.     case WP_BFG:
  504.         weaponInfo->readySound = trap_S_RegisterSound( "sound/weapons/bfg/bfg_hum.wav" );
  505.         MAKERGB( weaponInfo->flashDlightColor, 1, 0.7, 1 );
  506.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/bfg/bfg_fire.wav" );
  507.         cgs.media.bfgExplosionShader = trap_R_RegisterShader( "bfgExplosion" );
  508.         weaponInfo->missileModel = trap_R_RegisterModel( "models/weaphits/bfg.md3" );
  509.         weaponInfo->missileSound = trap_S_RegisterSound( "sound/weapons/rocket/rockfly.wav" );
  510.         break;
  511.  
  512.      default:
  513.         MAKERGB( weaponInfo->flashDlightColor, 1, 1, 1 );
  514.         weaponInfo->flashSound[0] = trap_S_RegisterSound( "sound/weapons/rocket/rocklf1a.wav" );
  515.         break;
  516.     }
  517. }
  518.  
  519. /*
  520. =================
  521. CG_RegisterItemVisuals
  522.  
  523. The server says this item is used on this level
  524. =================
  525. */
  526. void CG_RegisterItemVisuals( int itemNum ) {
  527.     itemInfo_t        *itemInfo;
  528.     gitem_t            *item;
  529.  
  530.     itemInfo = &cg_items[ itemNum ];
  531.     if ( itemInfo->registered ) {
  532.         return;
  533.     }
  534.  
  535.     item = &bg_itemlist[ itemNum ];
  536.  
  537.     memset( itemInfo, 0, sizeof( &itemInfo ) );
  538.     itemInfo->registered = qtrue;
  539.  
  540.     itemInfo->models[0] = trap_R_RegisterModel( item->world_model[0] );
  541.  
  542.     itemInfo->icon = trap_R_RegisterShader( item->icon );
  543.  
  544.     if ( item->giType == IT_WEAPON ) {
  545.         CG_RegisterWeapon( item->giTag );
  546.     }
  547.  
  548.     //
  549.     // powerups have an accompanying ring or sphere
  550.     //
  551.     if ( item->giType == IT_POWERUP || item->giType == IT_HEALTH || 
  552.         item->giType == IT_ARMOR || item->giType == IT_HOLDABLE ) {
  553.         if ( item->world_model[1] ) {
  554.             itemInfo->models[1] = trap_R_RegisterModel( item->world_model[1] );
  555.         }
  556.     }
  557. }
  558.  
  559.  
  560. /*
  561. ========================================================================================
  562.  
  563. VIEW WEAPON
  564.  
  565. ========================================================================================
  566. */
  567.  
  568. /*
  569. =================
  570. CG_MapTorsoToWeaponFrame
  571.  
  572. =================
  573. */
  574. static int CG_MapTorsoToWeaponFrame( clientInfo_t *ci, int frame ) {
  575.  
  576.     // change weapon
  577.     if ( frame >= ci->animations[TORSO_DROP].firstFrame 
  578.         && frame < ci->animations[TORSO_DROP].firstFrame + 9 ) {
  579.         return frame - ci->animations[TORSO_DROP].firstFrame + 6;
  580.     }
  581.  
  582.     // stand attack
  583.     if ( frame >= ci->animations[TORSO_ATTACK].firstFrame 
  584.         && frame < ci->animations[TORSO_ATTACK].firstFrame + 6 ) {
  585.         return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame;
  586.     }
  587.  
  588.     // stand attack 2
  589.     if ( frame >= ci->animations[TORSO_ATTACK2].firstFrame 
  590.         && frame < ci->animations[TORSO_ATTACK2].firstFrame + 6 ) {
  591.         return 1 + frame - ci->animations[TORSO_ATTACK].firstFrame;
  592.     }
  593.     
  594.     return 0;
  595. }
  596.  
  597.  
  598. /*
  599. ==============
  600. CG_CalculateWeaponPosition
  601. ==============
  602. */
  603. static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) {
  604.     float    scale;
  605.     int        delta;
  606.     float    fracsin;
  607.  
  608.     VectorCopy( cg.refdef.vieworg, origin );
  609.     VectorCopy( cg.refdefViewAngles, angles );
  610.  
  611.     // on odd legs, invert some angles
  612.     if ( cg.bobcycle & 1 ) {
  613.         scale = -cg.xyspeed;
  614.     } else {
  615.         scale = cg.xyspeed;
  616.     }
  617.  
  618.     // gun angles from bobbing
  619.     angles[ROLL] += scale * cg.bobfracsin * 0.005;
  620.     angles[YAW] += scale * cg.bobfracsin * 0.01;
  621.     angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
  622.  
  623.     // drop the weapon when landing
  624.     delta = cg.time - cg.landTime;
  625.     if ( delta < LAND_DEFLECT_TIME ) {
  626.         origin[2] += cg.landChange*0.25 * delta / LAND_DEFLECT_TIME;
  627.     } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  628.         origin[2] += cg.landChange*0.25 * 
  629.             (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME;
  630.     }
  631.  
  632. #if 0
  633.     // drop the weapon when stair climbing
  634.     delta = cg.time - cg.stepTime;
  635.     if ( delta < STEP_TIME/2 ) {
  636.         origin[2] -= cg.stepChange*0.25 * delta / (STEP_TIME/2);
  637.     } else if ( delta < STEP_TIME ) {
  638.         origin[2] -= cg.stepChange*0.25 * (STEP_TIME - delta) / (STEP_TIME/2);
  639.     }
  640. #endif
  641.  
  642.     // idle drift
  643.     scale = cg.xyspeed + 40;
  644.     fracsin = sin( cg.time * 0.001 );
  645.     angles[ROLL] += scale * fracsin * 0.01;
  646.     angles[YAW] += scale * fracsin * 0.01;
  647.     angles[PITCH] += scale * fracsin * 0.01;
  648. }
  649.  
  650.  
  651. /*
  652. ===============
  653. CG_LightningBolt
  654.  
  655. Origin will be the exact tag point, which is slightly
  656. different than the muzzle point used for determining hits.
  657. The cent should be the non-predicted cent if it is from the player,
  658. so the endpoint will reflect the simulated strike (lagging the predicted
  659. angle)
  660. ===============
  661. */
  662. static void CG_LightningBolt( centity_t *cent, vec3_t origin ) {
  663.     trace_t        trace;
  664.     refEntity_t        beam;
  665.     vec3_t            forward;
  666.     vec3_t            muzzlePoint, endPoint;
  667.  
  668.     if ( cent->currentState.weapon != WP_LIGHTNING ) {
  669.         return;
  670.     }
  671.  
  672.     memset( &beam, 0, sizeof( beam ) );
  673.  
  674.     // find muzzle point for this frame
  675.     VectorCopy( cent->lerpOrigin, muzzlePoint );
  676.     AngleVectors( cent->lerpAngles, forward, NULL, NULL );
  677.  
  678.     // FIXME: crouch
  679.     muzzlePoint[2] += DEFAULT_VIEWHEIGHT;
  680.  
  681.     VectorMA( muzzlePoint, 14, forward, muzzlePoint );
  682.  
  683.     // project forward by the lightning range
  684.     VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint );
  685.  
  686.     // see if it hit a wall
  687.     CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, 
  688.         cent->currentState.number, MASK_SHOT );
  689.  
  690.     // this is the endpoint
  691.     VectorCopy( trace.endpos, beam.oldorigin );
  692.  
  693.     // use the provided origin, even though it may be slightly
  694.     // different than the muzzle origin
  695.     VectorCopy( origin, beam.origin );
  696.  
  697.     beam.reType = RT_LIGHTNING;
  698.     beam.customShader = cgs.media.lightningShader;
  699.     trap_R_AddRefEntityToScene( &beam );
  700.  
  701.     // add the impact flare if it hit something
  702.     if ( trace.fraction < 1.0 ) {
  703.         vec3_t    angles;
  704.         vec3_t    dir;
  705.  
  706.         VectorSubtract( beam.oldorigin, beam.origin, dir );
  707.         VectorNormalize( dir );
  708.  
  709.         memset( &beam, 0, sizeof( beam ) );
  710.         beam.hModel = cgs.media.lightningExplosionModel;
  711.  
  712.         VectorMA( trace.endpos, -16, dir, beam.origin );
  713.  
  714.         // make a random orientation
  715.         angles[0] = rand() % 360;
  716.         angles[1] = rand() % 360;
  717.         angles[2] = rand() % 360;
  718.         AnglesToAxis( angles, beam.axis );
  719.         trap_R_AddRefEntityToScene( &beam );
  720.     }
  721. }
  722.  
  723.  
  724. /*
  725. ===============
  726. CG_SpawnRailTrail
  727.  
  728. Origin will be the exact tag point, which is slightly
  729. different than the muzzle point used for determining hits.
  730. ===============
  731. */
  732. static void CG_SpawnRailTrail( centity_t *cent, vec3_t origin ) {
  733.     clientInfo_t    *ci;
  734.  
  735.     if ( cent->currentState.weapon != WP_RAILGUN ) {
  736.         return;
  737.     }
  738.     if ( !cent->pe.railgunFlash ) {
  739.         return;
  740.     }
  741.     cent->pe.railgunFlash = qtrue;
  742.     ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  743.     CG_RailTrail( ci, origin, cent->pe.railgunImpact );
  744. }
  745.  
  746.  
  747. /*
  748. ======================
  749. CG_MachinegunSpinAngle
  750. ======================
  751. */
  752. #define        SPIN_SPEED    0.9
  753. #define        COAST_TIME    1000
  754. static float    CG_MachinegunSpinAngle( centity_t *cent ) {
  755.     int        delta;
  756.     float    angle;
  757.     float    speed;
  758.  
  759.     delta = cg.time - cent->pe.barrelTime;
  760.     if ( cent->pe.barrelSpinning ) {
  761.         angle = cent->pe.barrelAngle + delta * SPIN_SPEED;
  762.     } else {
  763.         if ( delta > COAST_TIME ) {
  764.             delta = COAST_TIME;
  765.         }
  766.  
  767.         speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
  768.         angle = cent->pe.barrelAngle + delta * speed;
  769.     }
  770.  
  771.     if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) {
  772.         cent->pe.barrelTime = cg.time;
  773.         cent->pe.barrelAngle = AngleMod( angle );
  774.         cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING);
  775.     }
  776.  
  777.     return angle;
  778. }
  779.  
  780.  
  781. /*
  782. ========================
  783. CG_AddWeaponWithPowerups
  784. ========================
  785. */
  786. static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups ) {
  787.     // add powerup effects
  788.     if ( powerups & ( 1 << PW_INVIS ) ) {
  789.         gun->customShader = cgs.media.invisShader;
  790.         trap_R_AddRefEntityToScene( gun );
  791.     } else {
  792.         trap_R_AddRefEntityToScene( gun );
  793.  
  794.         if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
  795.             gun->customShader = cgs.media.battleWeaponShader;
  796.             trap_R_AddRefEntityToScene( gun );
  797.         }
  798.         if ( powerups & ( 1 << PW_QUAD ) ) {
  799.             gun->customShader = cgs.media.quadWeaponShader;
  800.             trap_R_AddRefEntityToScene( gun );
  801.         }
  802.     }
  803. }
  804.  
  805.  
  806. /*
  807. =============
  808. CG_AddPlayerWeapon
  809.  
  810. Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL)
  811. The main player will have this called for BOTH cases, so effects like light and
  812. sound should only be done on the world model case.
  813. =============
  814. */
  815. void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent ) {
  816.     refEntity_t    gun;
  817.     refEntity_t    barrel;
  818.     refEntity_t    flash;
  819.     vec3_t        angles;
  820.     weapon_t    weaponNum;
  821.     weaponInfo_t    *weapon;
  822.     centity_t    *nonPredictedCent;
  823.  
  824.     weaponNum = cent->currentState.weapon;
  825.  
  826.     CG_RegisterWeapon( weaponNum );
  827.     weapon = &cg_weapons[weaponNum];
  828.  
  829.     // add the weapon
  830.     memset( &gun, 0, sizeof( gun ) );
  831.     VectorCopy( parent->lightingOrigin, gun.lightingOrigin );
  832.     gun.shadowPlane = parent->shadowPlane;
  833.     gun.renderfx = parent->renderfx;
  834.  
  835.     // set custom shading for railgun refire rate
  836.     if ( ps ) {
  837.         if ( cg.predictedPlayerState.weapon == WP_RAILGUN 
  838.             && cg.predictedPlayerState.weaponstate == WEAPON_FIRING ) {
  839.             float    f;
  840.  
  841.             f = (float)cg.predictedPlayerState.weaponTime / 1500;
  842.             gun.shaderRGBA[1] = 0;
  843.             gun.shaderRGBA[0] = 
  844.             gun.shaderRGBA[2] = 255 * ( 1.0 - f );
  845.         } else {
  846.             gun.shaderRGBA[0] = 255;
  847.             gun.shaderRGBA[1] = 255;
  848.             gun.shaderRGBA[2] = 255;
  849.             gun.shaderRGBA[3] = 255;
  850.         }
  851.     }
  852.  
  853.     gun.hModel = weapon->weaponModel;
  854.     if (!gun.hModel) {
  855.         return;
  856.     }
  857.  
  858.     if ( !ps ) {
  859.         // add weapon ready sound
  860.         cent->pe.lightningFiring = qfalse;
  861.         if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) {
  862.             // lightning gun and guantlet make a different sound when fire is held down
  863.             trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->firingSound );
  864.             cent->pe.lightningFiring = qtrue;
  865.         } else if ( weapon->readySound ) {
  866.             trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->readySound );
  867.         }
  868.     }
  869.  
  870.     CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon");
  871.  
  872.     CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups );
  873.  
  874.     // add the spinning barrel
  875.     if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
  876.         memset( &barrel, 0, sizeof( barrel ) );
  877.         VectorCopy( parent->lightingOrigin, barrel.lightingOrigin );
  878.         barrel.shadowPlane = parent->shadowPlane;
  879.         barrel.renderfx = parent->renderfx;
  880.  
  881.         barrel.hModel = weapon->barrelModel;
  882.         angles[YAW] = 0;
  883.         angles[PITCH] = 0;
  884.         angles[ROLL] = CG_MachinegunSpinAngle( cent );
  885.         AnglesToAxis( angles, barrel.axis );
  886.  
  887.         CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" );
  888.  
  889.         CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups );
  890.     }
  891.  
  892.     // make sure we aren't looking at cg.predictedPlayerEntity for LG
  893.     nonPredictedCent = &cg_entities[cent->currentState.clientNum];
  894.  
  895.     // if the index of the nonPredictedCent is not the same as the clientNum
  896.     // then this is a fake player (like on teh single player podiums), so
  897.     // go ahead and use the cent
  898.     if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) {
  899.         nonPredictedCent = cent;
  900.     }
  901.  
  902.     // add the flash
  903.     if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK )
  904.         && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) 
  905.     {
  906.         // continuous flash
  907.     } else {
  908.         // impulse flash
  909.         if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) {
  910.             return;
  911.         }
  912.     }
  913.  
  914.     memset( &flash, 0, sizeof( flash ) );
  915.     VectorCopy( parent->lightingOrigin, flash.lightingOrigin );
  916.     flash.shadowPlane = parent->shadowPlane;
  917.     flash.renderfx = parent->renderfx;
  918.  
  919.     flash.hModel = weapon->flashModel;
  920.     if (!flash.hModel) {
  921.         return;
  922.     }
  923.     angles[YAW] = 0;
  924.     angles[PITCH] = 0;
  925.     angles[ROLL] = crandom() * 10;
  926.     AnglesToAxis( angles, flash.axis );
  927.  
  928.     // colorize the railgun blast
  929.     if ( weaponNum == WP_RAILGUN ) {
  930.         clientInfo_t    *ci;
  931.  
  932.         ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  933.         flash.shaderRGBA[0] = 255 * ci->color[0];
  934.         flash.shaderRGBA[1] = 255 * ci->color[1];
  935.         flash.shaderRGBA[2] = 255 * ci->color[2];
  936.     }
  937.  
  938.     CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash");
  939.     trap_R_AddRefEntityToScene( &flash );
  940.  
  941.     if ( ps || cg.renderingThirdPerson ||
  942.         cent->currentState.number != cg.predictedPlayerState.clientNum ) {
  943.         // add lightning bolt
  944.         CG_LightningBolt( nonPredictedCent, flash.origin );
  945.  
  946.         // add rail trail
  947.         CG_SpawnRailTrail( cent, flash.origin );
  948.  
  949.         // make a dlight for the flash
  950.         if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) {
  951.             trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), weapon->flashDlightColor[0],
  952.                 weapon->flashDlightColor[1], weapon->flashDlightColor[2] );
  953.         }
  954.     }
  955. }
  956.  
  957. /*
  958. ==============
  959. CG_AddViewWeapon
  960.  
  961. Add the weapon, and flash for the player's view
  962. ==============
  963. */
  964. void CG_AddViewWeapon( playerState_t *ps ) {
  965.     refEntity_t    hand;
  966.     centity_t    *cent;
  967.     clientInfo_t    *ci;
  968.     float        fovOffset;
  969.     vec3_t        angles;
  970.     weaponInfo_t    *weapon;
  971.  
  972.     if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
  973.         return;
  974.     }
  975.  
  976.     if ( ps->pm_type == PM_INTERMISSION ) {
  977.         return;
  978.     }
  979.  
  980.     // no gun if in third person view
  981.     if ( cg.renderingThirdPerson ) {
  982.         return;
  983.     }
  984.  
  985.     // allow the gun to be completely removed
  986.     if ( !cg_drawGun.integer ) {
  987.         vec3_t        origin;
  988.  
  989.         if ( cg.predictedPlayerState.eFlags & EF_FIRING ) {
  990.             // special hack for lightning gun...
  991.             VectorCopy( cg.refdef.vieworg, origin );
  992.             VectorMA( origin, -8, cg.refdef.viewaxis[2], origin );
  993.             CG_LightningBolt( &cg_entities[ps->clientNum], origin );
  994.         }
  995.         return;
  996.     }
  997.  
  998.     // don't draw if testing a gun model
  999.     if ( cg.testGun ) {
  1000.         return;
  1001.     }
  1002.  
  1003.     // drop gun lower at higher fov
  1004.     if ( cg_fov.integer > 90 ) {
  1005.         fovOffset = -0.2 * ( cg_fov.integer - 90 );
  1006.     } else {
  1007.         fovOffset = 0;
  1008.     }
  1009.  
  1010.     cent = &cg.predictedPlayerEntity;    // &cg_entities[cg.snap->ps.clientNum];
  1011.     CG_RegisterWeapon( ps->weapon );
  1012.     weapon = &cg_weapons[ ps->weapon ];
  1013.  
  1014.     memset (&hand, 0, sizeof(hand));
  1015.  
  1016.     // set up gun position
  1017.     CG_CalculateWeaponPosition( hand.origin, angles );
  1018.  
  1019.     VectorMA( hand.origin, cg_gun_x.value, cg.refdef.viewaxis[0], hand.origin );
  1020.     VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin );
  1021.     VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin );
  1022.  
  1023.     AnglesToAxis( angles, hand.axis );
  1024.  
  1025.     // map torso animations to weapon animations
  1026.     if ( cg_gun_frame.integer ) {
  1027.         // development tool
  1028.         hand.frame = hand.oldframe = cg_gun_frame.integer;
  1029.         hand.backlerp = 0;
  1030.     } else {
  1031.         // get clientinfo for animation map
  1032.         ci = &cgs.clientinfo[ cent->currentState.clientNum ];
  1033.         hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame );
  1034.         hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame );
  1035.         hand.backlerp = cent->pe.torso.backlerp;
  1036.     }
  1037.  
  1038.     hand.hModel = weapon->handsModel;
  1039.     hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT;
  1040.  
  1041.     // add everything onto the hand
  1042.     CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity );
  1043. }
  1044.  
  1045. /*
  1046. ==============================================================================
  1047.  
  1048. WEAPON SELECTION
  1049.  
  1050. ==============================================================================
  1051. */
  1052.  
  1053. /*
  1054. ===================
  1055. CG_DrawWeaponSelect
  1056. ===================
  1057. */
  1058. void CG_DrawWeaponSelect( void ) {
  1059.     int        i;
  1060.     int        bits;
  1061.     int        count;
  1062.     int        x, y, w;
  1063.     char    *name;
  1064.     float    *color;
  1065.  
  1066.     // don't display if dead
  1067.     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
  1068.         return;
  1069.     }
  1070.  
  1071.     color = CG_FadeColor( cg.weaponSelectTime, WEAPON_SELECT_TIME );
  1072.     if ( !color ) {
  1073.         return;
  1074.     }
  1075.     trap_R_SetColor( color );
  1076.  
  1077.     // showing weapon select clears pickup item display, but not the blend blob
  1078.     cg.itemPickupTime = 0;
  1079.  
  1080.     // count the number of weapons owned
  1081.     bits = cg.snap->ps.stats[ STAT_WEAPONS ];
  1082.     count = 0;
  1083.     for ( i = 1 ; i < 16 ; i++ ) {
  1084.         if ( bits & ( 1 << i ) ) {
  1085.             count++;
  1086.         }
  1087.     }
  1088.  
  1089.     x = 320 - count * 20;
  1090.     y = 380;
  1091.  
  1092.     for ( i = 1 ; i < 16 ; i++ ) {
  1093.         if ( !( bits & ( 1 << i ) ) ) {
  1094.             continue;
  1095.         }
  1096.  
  1097.         CG_RegisterWeapon( i );
  1098.  
  1099.         // draw weapon icon
  1100.         CG_DrawPic( x, y, 32, 32, cg_weapons[i].weaponIcon );
  1101.  
  1102.         // draw selection marker
  1103.         if ( i == cg.weaponSelect ) {
  1104.             CG_DrawPic( x-4, y-4, 40, 40, cgs.media.selectShader );
  1105.         }
  1106.  
  1107.         // no ammo cross on top
  1108.         if ( !cg.snap->ps.ammo[ i ] ) {
  1109.             CG_DrawPic( x, y, 32, 32, cgs.media.noammoShader );
  1110.         }
  1111.  
  1112.         x += 40;
  1113.     }
  1114.  
  1115.     // draw the selected name
  1116.     if ( cg_weapons[ cg.weaponSelect ].item ) {
  1117.         name = cg_weapons[ cg.weaponSelect ].item->pickup_name;
  1118.         if ( name ) {
  1119.             w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
  1120.             x = ( SCREEN_WIDTH - w ) / 2;
  1121.             CG_DrawBigStringColor(x, y - 22, name, color);
  1122.         }
  1123.     }
  1124.  
  1125.     trap_R_SetColor( NULL );
  1126. }
  1127.  
  1128.  
  1129. /*
  1130. ===============
  1131. CG_WeaponSelectable
  1132. ===============
  1133. */
  1134. static qboolean CG_WeaponSelectable( int i ) {
  1135.     if ( !cg.snap->ps.ammo[i] ) {
  1136.         return qfalse;
  1137.     }
  1138.     if ( ! (cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ) ) ) {
  1139.         return qfalse;
  1140.     }
  1141.  
  1142.     return qtrue;
  1143. }
  1144.  
  1145. /*
  1146. ===============
  1147. CG_NextWeapon_f
  1148. ===============
  1149. */
  1150. void CG_NextWeapon_f( void ) {
  1151.     int        i;
  1152.     int        original;
  1153.  
  1154.     if ( !cg.snap ) {
  1155.         return;
  1156.     }
  1157.     if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1158.         return;
  1159.     }
  1160.  
  1161.     cg.weaponSelectTime = cg.time;
  1162.     original = cg.weaponSelect;
  1163.  
  1164.     for ( i = 0 ; i < 16 ; i++ ) {
  1165.         cg.weaponSelect++;
  1166.         if ( cg.weaponSelect == 16 ) {
  1167.             cg.weaponSelect = 0;
  1168.         }
  1169.         if ( cg.weaponSelect == WP_GAUNTLET ) {
  1170.             continue;        // never cycle to gauntlet
  1171.         }
  1172.         if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
  1173.             break;
  1174.         }
  1175.     }
  1176.     if ( i == 16 ) {
  1177.         cg.weaponSelect = original;
  1178.     }
  1179. }
  1180.  
  1181. /*
  1182. ===============
  1183. CG_PrevWeapon_f
  1184. ===============
  1185. */
  1186. void CG_PrevWeapon_f( void ) {
  1187.     int        i;
  1188.     int        original;
  1189.  
  1190.     if ( !cg.snap ) {
  1191.         return;
  1192.     }
  1193.     if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1194.         return;
  1195.     }
  1196.  
  1197.     cg.weaponSelectTime = cg.time;
  1198.     original = cg.weaponSelect;
  1199.  
  1200.     for ( i = 0 ; i < 16 ; i++ ) {
  1201.         cg.weaponSelect--;
  1202.         if ( cg.weaponSelect == -1 ) {
  1203.             cg.weaponSelect = 15;
  1204.         }
  1205.         if ( cg.weaponSelect == WP_GAUNTLET ) {
  1206.             continue;        // never cycle to gauntlet
  1207.         }
  1208.         if ( CG_WeaponSelectable( cg.weaponSelect ) ) {
  1209.             break;
  1210.         }
  1211.     }
  1212.     if ( i == 16 ) {
  1213.         cg.weaponSelect = original;
  1214.     }
  1215. }
  1216.  
  1217. /*
  1218. ===============
  1219. CG_Weapon_f
  1220. ===============
  1221. */
  1222. void CG_Weapon_f( void ) {
  1223.     int        num;
  1224.  
  1225.     if ( !cg.snap ) {
  1226.         return;
  1227.     }
  1228.     if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
  1229.         return;
  1230.     }
  1231.  
  1232.     num = atoi( CG_Argv( 1 ) );
  1233.  
  1234.     if ( num < 1 || num > 15 ) {
  1235.         return;
  1236.     }
  1237.  
  1238.     cg.weaponSelectTime = cg.time;
  1239.  
  1240.     if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) {
  1241.         return;        // don't have the weapon
  1242.     }
  1243.  
  1244.     cg.weaponSelect = num;
  1245. }
  1246.  
  1247. /*
  1248. ===================
  1249. CG_OutOfAmmoChange
  1250.  
  1251. The current weapon has just run out of ammo
  1252. ===================
  1253. */
  1254. void CG_OutOfAmmoChange( void ) {
  1255.     int        i;
  1256.  
  1257.     cg.weaponSelectTime = cg.time;
  1258.  
  1259.     for ( i = 15 ; i > 0 ; i-- ) {
  1260.         if ( CG_WeaponSelectable( i ) ) {
  1261.             cg.weaponSelect = i;
  1262.             break;
  1263.         }
  1264.     }
  1265. }
  1266.  
  1267.  
  1268.  
  1269. /*
  1270. ===================================================================================================
  1271.  
  1272. WEAPON EVENTS
  1273.  
  1274. ===================================================================================================
  1275. */
  1276.  
  1277. /*
  1278. ================
  1279. CG_FireWeapon
  1280.  
  1281. Caused by an EV_FIRE_WEAPON event
  1282. ================
  1283. */
  1284. void CG_FireWeapon( centity_t *cent ) {
  1285.     entityState_t *ent;
  1286.     int                c;
  1287.     weaponInfo_t    *weap;
  1288.  
  1289.     ent = ¢->currentState;
  1290.     if ( ent->weapon == WP_NONE ) {
  1291.         return;
  1292.     }
  1293.     if ( ent->weapon >= WP_NUM_WEAPONS ) {
  1294.         CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" );
  1295.         return;
  1296.     }
  1297.     weap = &cg_weapons[ ent->weapon ];
  1298.  
  1299.     // mark the entity as muzzle flashing, so when it is added it will
  1300.     // append the flash to the weapon model
  1301.     cent->muzzleFlashTime = cg.time;
  1302.  
  1303.     // lightning gun only does this this on initial press
  1304.     if ( ent->weapon == WP_LIGHTNING ) {
  1305.         if ( cent->pe.lightningFiring ) {
  1306.             return;
  1307.         }
  1308.     }
  1309.  
  1310.     // play quad sound if needed
  1311.     if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
  1312.         trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.media.quadSound );
  1313.     }
  1314.  
  1315.     // play a sound
  1316.     for ( c = 0 ; c < 4 ; c++ ) {
  1317.         if ( !weap->flashSound[c] ) {
  1318.             break;
  1319.         }
  1320.     }
  1321.     if ( c > 0 ) {
  1322.         c = rand() % c;
  1323.         if ( weap->flashSound[c] )
  1324.         {
  1325.             trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, weap->flashSound[c] );
  1326.         }
  1327.     }
  1328.  
  1329.     // do brass ejection
  1330.     if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) {
  1331.         weap->ejectBrassFunc( cent );
  1332.     }
  1333. }
  1334.  
  1335.  
  1336. /*
  1337. =================
  1338. CG_MissileHitWall
  1339.  
  1340. Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing
  1341. =================
  1342. */
  1343. void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir ) {
  1344.     qhandle_t        mod;
  1345.     qhandle_t        mark;
  1346.     qhandle_t        shader;
  1347.     sfxHandle_t        sfx;
  1348.     float            radius;
  1349.     float            light;
  1350.     vec3_t            lightColor;
  1351.     localEntity_t    *le;
  1352.     int                r;
  1353.     qboolean        alphaFade;
  1354.     qboolean        isSprite;
  1355.     int                duration;
  1356.  
  1357.     mark = 0;
  1358.     radius = 32;
  1359.     sfx = 0;
  1360.     mod = 0;
  1361.     shader = 0;
  1362.     light = 0;
  1363.     lightColor[0] = 1;
  1364.     lightColor[1] = 1;
  1365.     lightColor[2] = 0;
  1366.  
  1367.     // set defaults
  1368.     isSprite = qfalse;
  1369.     duration = 600;
  1370.  
  1371.     switch ( weapon ) {
  1372.     default:
  1373.     case WP_LIGHTNING:
  1374.         // no explosion at LG impact, it is added with the beam
  1375.         r = rand() & 3;
  1376.         if ( r < 2 ) {
  1377.             sfx = cgs.media.sfx_lghit2;
  1378.         } else if ( r == 2 ) {
  1379.             sfx = cgs.media.sfx_lghit1;
  1380.         } else {
  1381.             sfx = cgs.media.sfx_lghit3;
  1382.         }
  1383.         mark = cgs.media.holeMarkShader;
  1384.         radius = 12;
  1385.         break;
  1386.     case WP_GRENADE_LAUNCHER:
  1387.         mod = cgs.media.dishFlashModel;
  1388.         shader = cgs.media.grenadeExplosionShader;
  1389.         sfx = cgs.media.sfx_rockexp;
  1390.         mark = cgs.media.burnMarkShader;
  1391.         radius = 64;
  1392.         light = 300;
  1393.         isSprite = qtrue;
  1394.         break;
  1395.     case WP_ROCKET_LAUNCHER:
  1396.         mod = cgs.media.dishFlashModel;
  1397.         shader = cgs.media.rocketExplosionShader;
  1398.         sfx = cgs.media.sfx_rockexp;
  1399.         mark = cgs.media.burnMarkShader;
  1400.         radius = 64;
  1401.         light = 300;
  1402.         isSprite = qtrue;
  1403.         duration = 1000;
  1404.         lightColor[0] = 1;
  1405.         lightColor[1] = 0.75;
  1406.         lightColor[2] = 0.0;
  1407.         break;
  1408.     case WP_RAILGUN:
  1409.         mod = cgs.media.ringFlashModel;
  1410.         shader = cgs.media.railExplosionShader;
  1411.         sfx = cgs.media.sfx_plasmaexp;
  1412.         mark = cgs.media.energyMarkShader;
  1413.         radius = 24;
  1414.         break;
  1415.     case WP_PLASMAGUN:
  1416.         mod = cgs.media.ringFlashModel;
  1417.         shader = cgs.media.plasmaExplosionShader;
  1418.         sfx = cgs.media.sfx_plasmaexp;
  1419.         mark = cgs.media.energyMarkShader;
  1420.         radius = 16;
  1421.         break;
  1422.     case WP_BFG:
  1423.         mod = cgs.media.dishFlashModel;
  1424.         shader = cgs.media.bfgExplosionShader;
  1425.         sfx = cgs.media.sfx_rockexp;
  1426.         mark = cgs.media.burnMarkShader;
  1427.         radius = 32;
  1428.         isSprite = qtrue;
  1429.         break;
  1430.     case WP_SHOTGUN:
  1431.         mod = cgs.media.bulletFlashModel;
  1432.         shader = cgs.media.bulletExplosionShader;
  1433.         mark = cgs.media.bulletMarkShader;
  1434.         sfx = 0;
  1435.         radius = 4;
  1436.         break;
  1437.  
  1438.     case WP_MACHINEGUN:
  1439.         mod = cgs.media.bulletFlashModel;
  1440.         shader = cgs.media.bulletExplosionShader;
  1441.         mark = cgs.media.bulletMarkShader;
  1442.  
  1443.         r = rand() & 3;
  1444.         if ( r < 2 ) {
  1445.             sfx = cgs.media.sfx_ric1;
  1446.         } else if ( r == 2 ) {
  1447.             sfx = cgs.media.sfx_ric2;
  1448.         } else {
  1449.             sfx = cgs.media.sfx_ric3;
  1450.         }
  1451.  
  1452.         radius = 8;
  1453.         break;
  1454.     }
  1455.  
  1456.     if ( sfx ) {
  1457.         trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, sfx );
  1458.     }
  1459.  
  1460.     //
  1461.     // create the explosion
  1462.     //
  1463.     if ( mod ) {
  1464.         le = CG_MakeExplosion( origin, dir, 
  1465.                                mod,    shader,
  1466.                                duration, isSprite );
  1467.         le->light = light;
  1468.         VectorCopy( lightColor, le->lightColor );
  1469.         if ( weapon == WP_RAILGUN ) {
  1470.             // colorize with client color
  1471.             VectorCopy( cgs.clientinfo[clientNum].color, le->color );
  1472.         }
  1473.     }
  1474.  
  1475.     //
  1476.     // impact mark
  1477.     //
  1478.     alphaFade = (mark == cgs.media.energyMarkShader);    // plasma fades alpha, all others fade color
  1479.     if ( weapon == WP_RAILGUN ) {
  1480.         float    *color;
  1481.  
  1482.         // colorize with client color
  1483.         color = cgs.clientinfo[clientNum].color;
  1484.         CG_ImpactMark( mark, origin, dir, random()*360, color[0],color[1], color[2],1, alphaFade, radius, qfalse );
  1485.     } else {
  1486.         CG_ImpactMark( mark, origin, dir, random()*360, 1,1,1,1, alphaFade, radius, qfalse );
  1487.     }
  1488. }
  1489.  
  1490.  
  1491. /*
  1492. =================
  1493. CG_MissileHitPlayer
  1494. =================
  1495. */
  1496. void CG_MissileHitPlayer( int weapon, vec3_t origin, vec3_t dir, int entityNum ) {
  1497.     CG_Bleed( origin, entityNum );
  1498.  
  1499.     // some weapons will make an explosion with the blood, while
  1500.     // others will just make the blood
  1501.     switch ( weapon ) {
  1502.     case WP_GRENADE_LAUNCHER:
  1503.     case WP_ROCKET_LAUNCHER:
  1504.         CG_MissileHitWall( weapon, 0, origin, dir );
  1505.         break;
  1506.     default:
  1507.         break;
  1508.     }
  1509. }
  1510.  
  1511.  
  1512.  
  1513. /*
  1514. ============================================================================
  1515.  
  1516. SHOTGUN TRACING
  1517.  
  1518. ============================================================================
  1519. */
  1520.  
  1521. /*
  1522. ================
  1523. CG_ShotgunPellet
  1524. ================
  1525. */
  1526. static void CG_ShotgunPellet( vec3_t start, vec3_t end, int skipNum ) {
  1527.     trace_t        tr;
  1528.     int sourceContentType, destContentType;
  1529.  
  1530.     CG_Trace( &tr, start, NULL, NULL, end, skipNum, MASK_SHOT );
  1531.  
  1532.     sourceContentType = trap_CM_PointContents( start, 0 );
  1533.     destContentType = trap_CM_PointContents( tr.endpos, 0 );
  1534.  
  1535.     // FIXME: should probably move this cruft into CG_BubbleTrail
  1536.     if ( sourceContentType == destContentType ) {
  1537.         if ( sourceContentType & CONTENTS_WATER ) {
  1538.             CG_BubbleTrail( start, tr.endpos, 32 );
  1539.         }
  1540.     } else if ( sourceContentType & CONTENTS_WATER ) {
  1541.         trace_t trace;
  1542.  
  1543.         trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
  1544.         CG_BubbleTrail( start, trace.endpos, 32 );
  1545.     } else if ( destContentType & CONTENTS_WATER ) {
  1546.         trace_t trace;
  1547.  
  1548.         trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
  1549.         CG_BubbleTrail( tr.endpos, trace.endpos, 32 );
  1550.     }
  1551.  
  1552.     if (  tr.surfaceFlags & SURF_NOIMPACT ) {
  1553.         return;
  1554.     }
  1555.  
  1556.     if ( cg_entities[tr.entityNum].currentState.eType == ET_PLAYER ) {
  1557.         CG_MissileHitPlayer( WP_SHOTGUN, tr.endpos, tr.plane.normal, tr.entityNum );
  1558.     } else {
  1559.         if ( tr.surfaceFlags & SURF_NOIMPACT ) {
  1560.             // SURF_NOIMPACT will not make a flame puff or a mark
  1561.             return;
  1562.         }
  1563.         CG_MissileHitWall( WP_SHOTGUN, 0, tr.endpos, tr.plane.normal );
  1564.     }
  1565. }
  1566.  
  1567. /*
  1568. ================
  1569. CG_ShotgunPattern
  1570.  
  1571. Perform the same traces the server did to locate the
  1572. hit splashes (FIXME: ranom seed isn't synce anymore)
  1573. ================
  1574. */
  1575. static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int otherEntNum ) {
  1576.     int            i;
  1577.     float        r, u;
  1578.     vec3_t        end;
  1579.     vec3_t        forward, right, up;
  1580.  
  1581.     // derive the right and up vectors from the forward vector, because
  1582.     // the client won't have any other information
  1583.     VectorNormalize2( origin2, forward );
  1584.     PerpendicularVector( right, forward );
  1585.     CrossProduct( forward, right, up );
  1586.  
  1587.     // generate the "random" spread pattern
  1588.     for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) {
  1589.         r = crandom() * DEFAULT_SHOTGUN_SPREAD;
  1590.         u = crandom() * DEFAULT_SHOTGUN_SPREAD;
  1591.         VectorMA( origin, 8192, forward, end);
  1592.         VectorMA (end, r, right, end);
  1593.         VectorMA (end, u, up, end);
  1594.  
  1595.         CG_ShotgunPellet( origin, end, otherEntNum );
  1596.     }
  1597. }
  1598.  
  1599. /*
  1600. ==============
  1601. CG_ShotgunFire
  1602. ==============
  1603. */
  1604. void CG_ShotgunFire( entityState_t *es ) {
  1605.     vec3_t    v;
  1606.     int        contents;
  1607.  
  1608.     VectorSubtract( es->origin2, es->pos.trBase, v );
  1609.     VectorNormalize( v );
  1610.     VectorScale( v, 32, v );
  1611.     VectorAdd( es->pos.trBase, v, v );
  1612.     if ( cgs.glconfig.hardwareType != GLHW_RAGEPRO ) {
  1613.         // ragepro can't alpha fade, so don't even bother with smoke
  1614.         vec3_t            up;
  1615.  
  1616.         contents = trap_CM_PointContents( es->pos.trBase, 0 );
  1617.         if ( !( contents & CONTENTS_WATER ) ) {
  1618.             VectorSet( up, 0, 0, 8 );
  1619.             CG_SmokePuff( v, up, 32, 1, 1, 1, 0.33, 900, cg.time, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader );
  1620.         }
  1621.     }
  1622.     CG_ShotgunPattern( es->pos.trBase, es->origin2, es->otherEntityNum );
  1623. }
  1624.  
  1625. /*
  1626. ============================================================================
  1627.  
  1628. BULLETS
  1629.  
  1630. ============================================================================
  1631. */
  1632.  
  1633.  
  1634. /*
  1635. ===============
  1636. CG_Tracer
  1637. ===============
  1638. */
  1639. void CG_Tracer( vec3_t source, vec3_t dest ) {
  1640.     vec3_t        forward, right;
  1641.     polyVert_t    verts[4];
  1642.     vec3_t        line;
  1643.     float        len, begin, end;
  1644.     vec3_t        start, finish;
  1645.     vec3_t        midpoint;
  1646.  
  1647.     // tracer
  1648.     VectorSubtract( dest, source, forward );
  1649.     len = VectorNormalize( forward );
  1650.  
  1651.     // start at least a little ways from the muzzle
  1652.     if ( len < 100 ) {
  1653.         return;
  1654.     }
  1655.     begin = 50 + random() * (len - 60);
  1656.     end = begin + cg_tracerLength.value;
  1657.     if ( end > len ) {
  1658.         end = len;
  1659.     }
  1660.     VectorMA( source, begin, forward, start );
  1661.     VectorMA( source, end, forward, finish );
  1662.  
  1663.     line[0] = DotProduct( forward, cg.refdef.viewaxis[1] );
  1664.     line[1] = DotProduct( forward, cg.refdef.viewaxis[2] );
  1665.  
  1666.     VectorScale( cg.refdef.viewaxis[1], line[1], right );
  1667.     VectorMA( right, -line[0], cg.refdef.viewaxis[2], right );
  1668.     VectorNormalize( right );
  1669.  
  1670.     VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz );
  1671.     verts[0].st[0] = 0;
  1672.     verts[0].st[1] = 1;
  1673.     verts[0].modulate[0] = 255;
  1674.     verts[0].modulate[1] = 255;
  1675.     verts[0].modulate[2] = 255;
  1676.     verts[0].modulate[3] = 255;
  1677.  
  1678.     VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz );
  1679.     verts[1].st[0] = 1;
  1680.     verts[1].st[1] = 0;
  1681.     verts[1].modulate[0] = 255;
  1682.     verts[1].modulate[1] = 255;
  1683.     verts[1].modulate[2] = 255;
  1684.     verts[1].modulate[3] = 255;
  1685.  
  1686.     VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz );
  1687.     verts[2].st[0] = 1;
  1688.     verts[2].st[1] = 1;
  1689.     verts[2].modulate[0] = 255;
  1690.     verts[2].modulate[1] = 255;
  1691.     verts[2].modulate[2] = 255;
  1692.     verts[2].modulate[3] = 255;
  1693.  
  1694.     VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz );
  1695.     verts[3].st[0] = 0;
  1696.     verts[3].st[1] = 0;
  1697.     verts[3].modulate[0] = 255;
  1698.     verts[3].modulate[1] = 255;
  1699.     verts[3].modulate[2] = 255;
  1700.     verts[3].modulate[3] = 255;
  1701.  
  1702.     trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts );
  1703.  
  1704.     midpoint[0] = ( start[0] + finish[0] ) * 0.5;
  1705.     midpoint[1] = ( start[1] + finish[1] ) * 0.5;
  1706.     midpoint[2] = ( start[2] + finish[2] ) * 0.5;
  1707.  
  1708.     // add the tracer sound
  1709.     trap_S_StartSound( midpoint, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.tracerSound );
  1710.  
  1711. }
  1712.  
  1713.  
  1714. /*
  1715. ======================
  1716. CG_CalcMuzzlePoint
  1717. ======================
  1718. */
  1719. static qboolean    CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) {
  1720.     vec3_t        forward;
  1721.     centity_t    *cent;
  1722.     int            anim;
  1723.  
  1724.     if ( entityNum == cg.snap->ps.clientNum ) {
  1725.         VectorCopy( cg.snap->ps.origin, muzzle );
  1726.         muzzle[2] += cg.snap->ps.viewheight;
  1727.         AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL );
  1728.         VectorMA( muzzle, 14, forward, muzzle );
  1729.         return qtrue;
  1730.     }
  1731.  
  1732.     cent = &cg_entities[entityNum];
  1733.     if ( !cent->currentValid ) {
  1734.         return qfalse;
  1735.     }
  1736.  
  1737.     VectorCopy( cent->currentState.pos.trBase, muzzle );
  1738.  
  1739.     AngleVectors( cent->currentState.apos.trBase, forward, NULL, NULL );
  1740.     anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT;
  1741.     if ( anim == LEGS_WALKCR || anim == LEGS_IDLECR ) {
  1742.         muzzle[2] += CROUCH_VIEWHEIGHT;
  1743.     } else {
  1744.         muzzle[2] += DEFAULT_VIEWHEIGHT;
  1745.     }
  1746.  
  1747.     VectorMA( muzzle, 14, forward, muzzle );
  1748.  
  1749.     return qtrue;
  1750.  
  1751. }
  1752.  
  1753. /*
  1754. ======================
  1755. CG_Bullet
  1756.  
  1757. Renders bullet effects.
  1758. ======================
  1759. */
  1760. void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum ) {
  1761.     trace_t trace;
  1762.     int sourceContentType, destContentType;
  1763.     vec3_t        start;
  1764.  
  1765.     // if the shooter is currently valid, calc a source point and possibly
  1766.     // do trail effects
  1767.     if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) {
  1768.         if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) {
  1769.             sourceContentType = trap_CM_PointContents( start, 0 );
  1770.             destContentType = trap_CM_PointContents( end, 0 );
  1771.  
  1772.             // do a complete bubble trail if necessary
  1773.             if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) {
  1774.                 CG_BubbleTrail( start, end, 32 );
  1775.             }
  1776.             // bubble trail from water into air
  1777.             else if ( ( sourceContentType & CONTENTS_WATER ) ) {
  1778.                 trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
  1779.                 CG_BubbleTrail( start, trace.endpos, 32 );
  1780.             }
  1781.             // bubble trail from air into water
  1782.             else if ( ( destContentType & CONTENTS_WATER ) ) {
  1783.                 trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
  1784.                 CG_BubbleTrail( end, trace.endpos, 32 );
  1785.             }
  1786.  
  1787.             // draw a tracer
  1788.             if ( random() < cg_tracerChance.value ) {
  1789.                 CG_Tracer( start, end );
  1790.             }
  1791.         }
  1792.     }
  1793.  
  1794.     // impact splash and mark
  1795.     if ( flesh ) {
  1796.         CG_Bleed( end, fleshEntityNum );
  1797.     } else {
  1798.         CG_MissileHitWall( WP_MACHINEGUN, 0, end, normal );
  1799.     }
  1800.  
  1801. }
  1802.